// Copyright Abhijeet Nayak @2013
// Please contact abhijeet@cise.ufl.edu before copying the code

#include "TwoWayList.h"
#include "Record.h"
#include "Schema.h"
#include "File.h"
#include "Comparison.h"
#include "ComparisonEngine.h"
#include "DBFile.h"
#include "Defs.h"
#include <iostream>
#include <fstream>
#include <string.h>

extern "C" {
int yyparse(void);   // defined in y.tab.c
}

DBFile::DBFile() {
	NoOfPages = 0;
	CurrentPage = 0;
	f_path = NULL;
}

//Create the binary db file to store file class
int DBFile::Create(char *fpath, fType f_type, void *startup) {
	//Save the file path
	f_path = strdup(fpath);
	file.Open(0, f_path);
	file.Close();
}

//Save the data from the text file to binary db file
void DBFile::Load(Schema &f_schema, char *loadpath) {

	//Count the no of pages
	off_t pageCount = 0;

	//Check if Create is called and DB file is created before calling Load
	if (f_path != NULL) {

		//Open the binary file database to which data will be dumped
		file.Open(1, f_path);

		//Open the text database from which data will be fetched
		FILE *TextFile = fopen(loadpath, "r");

		//Clean the page before further processing
		page.EmptyItOut();

		//If text file opened successfully, start processing it
		if (TextFile != NULL) {

			//Read all the records from the text file
			while (record.SuckNextRecord(&f_schema, TextFile)) {

				//Write the records to the page
				if (!page.Append(&record)) {
					//Come here if page is full
					//add the full page to file and empty it's content
					file.AddPage(&page, pageCount);
					pageCount++;
					page.EmptyItOut();
					page.Append(&record);
				}
			}
			//Check if the page is empty, if not flush its content to DB
			if (!page.IsEmpty()) {
				file.AddPage(&page, pageCount);
				page.EmptyItOut();
			}
			cout << " pages written to disk : " << pageCount << endl;
		} else { //file failed to open
			cout << "filed to open the file" << endl;
		}
		//Finally close file
		Close();
	} else { //else of error for calling load without create
		cout << "First call create and then call Load to load the DB" << endl;
		//exit(1);
	}
}

//Open the binary db file for reading
int DBFile::Open(char *f_path) {
	//First check if the f_path is valid
	ifstream infile;
	infile.open(f_path, ifstream::in);

	if (!infile.good()) {
		cout << "File " << f_path << "does not exist" << endl;
		//Return failure
		return 1;
	} else {
		//Close the stream
		infile.close();
	}
	//Open the DB file
	file.Open(1, f_path);

	//Return Success
	return 0;
}

void DBFile::Add(Record &rec) {
	//Get total no of pages in the file
	off_t LastPage;
	int pages = file.GetLength();

	//GetLength will retun 0 if the db is empty
	//And return 2 if there is only 1 page and so on
	if (pages >= 2) {
		LastPage = pages - 2;
	}

	//If there is atleast one page in the file
	if (pages >= 2) {
		//Fetch the last page
		file.GetPage(&page, LastPage);

		//Add the record to the page and if it failes then create new page
		if (!page.Append(&rec)) {
			page.EmptyItOut();
			page.Append(&rec);
			LastPage++;
		}
	} else { //If the file is empty
		page.Append(&rec);
		LastPage = 0;
	}
	//Finally write the page to file
	file.AddPage(&page, LastPage);
}

//Close the binary db file
int DBFile::Close() {
	file.Close();
}

void DBFile::MoveFirst() {
	//Cleanup the page 
	page.EmptyItOut();
	//Load the page with first page data
	file.GetPage(&page, 0);
	//Set pagecounter
	CurrentPage = 0;
}

//Fetch records from the binary db file, 
//after it is open by Open function
int DBFile::GetNext(Record &fetchme) {
	//Total no of pages in the db
	NoOfPages = file.GetLength() - 1;

	//Incase of first fetch	
	if (CurrentPage == 0) {
		file.GetPage(&page, CurrentPage);
		CurrentPage++;
		GetNext(fetchme);
	} else {
		//Get record from page or go to else
		if (page.GetFirst(&fetchme)) {
			return 1;
		} else {
			//Check if there are more pages left in the file
			//Check if we are under 
			if (CurrentPage < NoOfPages) {
				file.GetPage(&page, CurrentPage);
				CurrentPage++;
				GetNext(fetchme);

			} else {
				return 0;
			}
		}
	}
}

int DBFile::GetNext(Record &fetchme, CNF &cnf, Record &literal) {

	Record temp;

	//Total no of pages in the db
	NoOfPages = file.GetLength() - 1;

	//Check if first page if fetch, if not fetch first page
	if (CurrentPage == 0) {
		file.GetPage(&page, CurrentPage);
		CurrentPage++;
		return (GetNext(fetchme, cnf, literal));
	} else {
		//Continue processing till there are more pages in the file
		while (CurrentPage <= NoOfPages) {
			//Fetch the record from class
			if (page.GetFirst(&temp)) {
				int cond = comp.Compare(&temp, &literal, &cnf);
				if (cond) {
					fetchme.Consume(&temp);
					return 1;
				} else {
					continue;
				}
			} else {
				if (CurrentPage < NoOfPages) {
					file.GetPage(&page, CurrentPage);
					CurrentPage++;
					return (GetNext(fetchme, cnf, literal));
				} else {
					return 0;
				}
			}
		}
	}
}
